"""
Wind Turbine Blade Data Analysis Framework
========================================

This framework helps analyze complex wind turbine blade data by managing multiple variables
and their relationships. It provides tools for data processing, analysis, and visualization.

Key Capabilities:
---------------
1. Variable Management
2. Correlation Analysis
3. Dimension Reduction
4. Feature Selection
5. Sensitivity Analysis

Variable Categories:
-----------------
1. Geometry Variables:
   - Blade length
   - Chord distribution
   - Twist angle
   - Thickness distribution
   - Airfoil shape parameters

2. Material Properties:
   - Elastic modulus
   - Density
   - Poisson's ratio
   - Fiber orientation
   - Layer thickness

3. Operating Conditions:
   - Wind speed
   - Rotor speed
   - Pitch angle
   - Yaw angle
   - Temperature

4. Response Variables:
   - Tip deflection
   - Root moment
   - Strain distribution
   - Natural frequencies
   - Stress distribution
   - Tower top forces and moments (x and y directions)
   - Tower base forces and moments (x and y directions)
   - Nacelle accelerations and moments (x and y directions)
"""

import warnings
try:
    import pandas as pd
    import numpy as np
    from sklearn.preprocessing import StandardScaler, MinMaxScaler
    from sklearn.decomposition import PCA
    from sklearn.feature_selection import SelectKBest, f_regression
    import seaborn as sns
    import matplotlib.pyplot as plt
    from typing import List, Dict, Tuple
except ImportError as e:
    warnings.warn(f"Failed to import required dependencies: {str(e)}")
    raise

from BeaverFramework.Framework.turbine_define import LAC, Tower, Nacelle, Blade

class BladeDataAnalyzer:
    """
    Main class for analyzing wind turbine blade data.
    
    This class provides methods to:
    - Load and organize data
    - Analyze correlations between variables
    - Reduce data dimensions
    - Select important features
    - Perform sensitivity analysis
    """

    def __init__(self):
        """
        Initialize the analyzer with predefined variable groups.
        Variables are categorized into geometry, material, operating, and responses.
        """
        self.data = None
        self.scaler = StandardScaler()
        # Define variable groups for better organization
        self.variable_groups = {
            'geometry': [
                'blade_length',      # Overall length of the blade
                'chord_distribution', # Chord length at different sections
                'twist_angle',       # Twist angle distribution
                'thickness_distribution', # Thickness along the blade
                'airfoil_shape'      # Airfoil shape parameters
            ],
            'material': [
                'elastic_modulus',   # Material stiffness
                'density',           # Material density
                'poisson_ratio',     # Material Poisson's ratio
                'fiber_orientation', # Composite fiber angles
                'layer_thickness'    # Composite layer thicknesses
            ],
            'operating': [
                'wind_speed',        # Incoming wind velocity
                'rotor_speed',       # Rotational speed
                'pitch_angle',       # Blade pitch angle
                'yaw_angle',         # Turbine yaw angle
                'temperature'        # Operating temperature
            ],
            'responses': [
                'tip_deflection',    # Blade tip displacement
                'root_moment',       # Moment at blade root
                'strain_distribution', # Strain along the blade
                'natural_frequencies', # Structural frequencies
                'stress_distribution', # Stress along the blade
                'tower_top_force_x',      # Tower top fore-aft force
                'tower_top_force_y',      # Tower top side-to-side force
                'tower_top_moment_x',     # Tower top fore-aft moment
                'tower_top_moment_y',     # Tower top side-to-side moment
                'tower_base_force_x',     # Tower base fore-aft force
                'tower_base_force_y',     # Tower base side-to-side force
                'tower_base_moment_x',    # Tower base fore-aft moment
                'tower_base_moment_y',    # Tower base side-to-side moment
                'nacelle_acceleration_x', # Nacelle acceleration fore-aft
                'nacelle_acceleration_y', # Nacelle acceleration side-to-side
                'nacelle_moment_x',      # Nacelle moment fore-aft
                'nacelle_moment_y'       # Nacelle moment side-to-side
            ]
        }

    def load_data(self, file_path: str) -> None:
        """
        Load data from various file formats (CSV, Excel, Parquet).
        
        Args:
            file_path (str): Path to the data file
            
        Example:
            analyzer.load_data('blade_data.csv')
        """
        try:
            if file_path.endswith('.csv'):
                self.data = pd.read_csv(file_path)
            elif file_path.endswith('.xlsx'):
                self.data = pd.read_excel(file_path)
            elif file_path.endswith('.parquet'):
                self.data = pd.read_parquet(file_path)
            print(f"Successfully loaded {len(self.data)} samples with {len(self.data.columns)} variables")
        except Exception as e:
            print(f"Error loading data: {e}")

    def analyze_correlations(self, threshold: float = 0.8) -> Dict[str, List[Tuple[str, str, float]]]:
        """
        Analyze correlations between variables within each group.
        
        Args:
            threshold (float): Correlation coefficient threshold (default: 0.8)
            
        Returns:
            Dictionary of high correlations by group
            
        Example:
            correlations = analyzer.analyze_correlations(threshold=0.7)
        """
        correlations = {}
        for group, variables in self.variable_groups.items():
            correlations[group] = []
            for i in range(len(variables)):
                for j in range(i + 1, len(variables)):
                    corr = np.corrcoef(self.data[variables[i]], self.data[variables[j]])[0, 1]
                    if abs(corr) > threshold:
                        correlations[group].append((variables[i], variables[j], corr))
        return correlations

def main():
    """
    Example usage of the BladeDataAnalyzer class.
    """
    # Create sample data
    np.random.seed(42)
    n_samples = 100
    
    # Get typical values from turbine_define classes
    blade = Blade()
    tower = Tower()
    nacelle = Nacelle()
    lac = LAC()
    
    sample_data = pd.DataFrame({
        # Geometry variables
        'blade_length': np.random.normal(blade.length, blade.length*0.1, n_samples),
        'chord_distribution': np.random.normal(np.mean(blade.chord), np.std(blade.chord), n_samples),
        'twist_angle': np.random.normal(np.mean(blade.twist), np.std(blade.twist), n_samples),
        'thickness_distribution': np.random.normal(np.mean(blade.thickness), np.std(blade.thickness), n_samples),
        'airfoil_shape': np.random.normal(0.3, 0.03, n_samples),
        
        # Material variables (using typical values if not in turbine_define)
        'elastic_modulus': np.random.normal(70e9, 5e9, n_samples),
        'density': np.random.normal(1800, 100, n_samples),
        'poisson_ratio': np.random.normal(0.3, 0.02, n_samples),
        'fiber_orientation': np.random.normal(45, 5, n_samples),
        'layer_thickness': np.random.normal(0.002, 0.0002, n_samples),
        
        # Operating variables
        'wind_speed': np.random.normal(lac.Turbine_ws_power_rated, 2, n_samples),
        'rotor_speed': np.random.normal(lac.Turbine_rs_rated, 1, n_samples),
        'pitch_angle': np.random.normal(5, 1, n_samples),
        'yaw_angle': np.random.normal(0, 2, n_samples),
        'temperature': np.random.normal(20, 5, n_samples),
        
        # Response variables
        'tip_deflection': np.random.normal(2, 0.3, n_samples),
        'root_moment': np.random.normal(np.mean(blade.load_ultimate_design), np.std(blade.load_ultimate_design), n_samples),
        'strain_distribution': np.random.normal(0.001, 0.0001, n_samples),
        'natural_frequencies': np.random.normal(1.5, 0.1, n_samples),
        'stress_distribution': np.random.normal(200e6, 20e6, n_samples),
        
        # Tower responses
        'tower_top_force_x': np.random.normal(np.mean(tower.load_ultimate_design), np.std(tower.load_ultimate_design)/10, n_samples),
        'tower_top_force_y': np.random.normal(np.mean(tower.load_ultimate_design)*0.6, np.std(tower.load_ultimate_design)/10, n_samples),
        'tower_top_moment_x': np.random.normal(np.mean(tower.load_ultimate_design), np.std(tower.load_ultimate_design)/10, n_samples),
        'tower_top_moment_y': np.random.normal(np.mean(tower.load_ultimate_design)*0.8, np.std(tower.load_ultimate_design)/10, n_samples),
        'tower_base_force_x': np.random.normal(np.mean(tower.load_ultimate_design)*1.5, np.std(tower.load_ultimate_design)/10, n_samples),
        'tower_base_force_y': np.random.normal(np.mean(tower.load_ultimate_design)*1.2, np.std(tower.load_ultimate_design)/10, n_samples),
        'tower_base_moment_x': np.random.normal(np.mean(tower.load_ultimate_design)*2.5, np.std(tower.load_ultimate_design)/10, n_samples),
        'tower_base_moment_y': np.random.normal(np.mean(tower.load_ultimate_design)*2.0, np.std(tower.load_ultimate_design)/10, n_samples),
        
        # Nacelle responses
        'nacelle_acceleration_x': np.random.normal(2.5, 0.25, n_samples),
        'nacelle_acceleration_y': np.random.normal(1.5, 0.15, n_samples),
        'nacelle_moment_x': np.random.normal(5e6, 5e5, n_samples),
        'nacelle_moment_y': np.random.normal(3e6, 3e5, n_samples)
    })
    
    # Save sample data to CSV
    sample_data.to_csv('sample_blade_data.csv', index=False)
if __name__ == "__main__":
    main()